/*
 * Copyright (c) 2006-2021, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2022-01-11     chenbin      the first version
 */
#include "drv_eth.h"
#ifdef BSP_USING_CAN
#include <netif/ethernetif.h>
#include <lwipopts.h>

#include "stdint.h"
#include "n32g45x.h"
#include "n32g45x_eth.h"

/* debug option */
//#define ETH_RX_DUMP
//#define ETH_TX_DUMP
//#define DRV_DEBUG
#define LOG_TAG             "drv.emac"
#include <drv_log.h>

#define MAX_ADDR_LEN 6

struct rt_n32g45x_eth
{
    /* inherit from ethernet device */
    struct eth_device parent;
#ifndef PHY_USING_INTERRUPT_MODE
    rt_timer_t poll_link_timer;
#endif

    /* interface address info, hw address */
    rt_uint8_t  dev_addr[MAX_ADDR_LEN];
		
		uint8_t 		eth_interface_mode;
		uint8_t 		eth_phy_type;
	
    uint16_t		phy_addr;
    uint16_t		phy_id;
    /* ETH_Speed */
    uint32_t    eth_link_speed;
    /* ETH_Duplex_Mode */
    uint32_t    eth_link_mode;
};
enum
{
    PHY_TYPE_LAN8710 = 0,
    PHY_TYPE_DP83848 = 1,
    PHY_TYPE_KSZ8041 = 2,
    PHY_TYPE_DM9161  = 3,
    PHY_TYPE_IP101GR  = 4,
};

#define ETH_RXBUFNB     5
#define ETH_TXBUFNB     5
#define ETH_RX_BUF_SIZE 1524
#define ETH_TX_BUF_SIZE 1524

static ETH_DMADescType g_RxDesc[ETH_RXBUFNB];
static ETH_DMADescType g_TxDesc[ETH_TXBUFNB];
static uint8_t g_RxBuf[ETH_RX_BUF_SIZE * ETH_RXBUFNB];
static uint8_t g_TxBuf[ETH_TX_BUF_SIZE * ETH_TXBUFNB];

static struct rt_n32g45x_eth n32g45x_eth_device = 
{
	.eth_interface_mode = ETH_INTERFACE_RMII,
	.eth_phy_type = PHY_TYPE_IP101GR,
	.eth_link_speed = ETH_SPEED_MODE_100M,
	.eth_link_mode  = ETH_DUPLEX_MODE_FULL
};

#if defined(ETH_RX_DUMP) || defined(ETH_TX_DUMP)
#define __is_print(ch) ((unsigned int)((ch) - ' ') < 127u - ' ')
static void dump_hex(const rt_uint8_t *ptr, rt_size_t buflen)
{
    unsigned char *buf = (unsigned char *)ptr;
    int i, j;

    for (i = 0; i < buflen; i += 16)
    {
        rt_kprintf("%08X: ", i);

        for (j = 0; j < 16; j++)
            if (i + j < buflen)
                rt_kprintf("%02X ", buf[i + j]);
            else
                rt_kprintf("   ");
        rt_kprintf(" ");

        for (j = 0; j < 16; j++)
            if (i + j < buflen)
                rt_kprintf("%c", __is_print(buf[i + j]) ? buf[i + j] : '.');
        rt_kprintf("\n");
    }
}
#endif

uint16_t phy_read_reg(uint16_t PHYAddress, uint16_t PHYReg)
{
	return ETH_ReadPhyRegister(PHYAddress,PHYReg);
}

uint32_t phy_write_reg(uint16_t PHYAddress, uint16_t PHYReg, uint16_t PHYValue)
{
	return ETH_WritePhyRegister(PHYAddress,PHYReg,PHYValue);
}


#define PHY_TYPE      PHY_TYPE_IP101GR
#define ETH_INTERFACE ETH_INTERFACE_RMII // 0:RMII, 1:MII
#define REMAP_MODE    0
/* Phy Initial-----------------------------------------------------------------*/
uint32_t InitPHY(ETH_InitType* ETH_InitStruct)
{
    unsigned short RegValue = 0;
    uint32_t timeout;
    unsigned short PHY_Address;
    uint16_t phy_status_reg, phy_speed_bit, phy_duplex_bit;

    // search PHY address
    for (PHY_Address = 0; PHY_Address < 32; ++PHY_Address)
    {
        uint16_t val = ETH_ReadPhyRegister(PHY_Address, 3); // PHY ID register
        if (val != 0x0000 && val != 0xFFFF)
        {
            LOG_I("PHY addr: %d", PHY_Address);
            break;
        }
    }
    if (PHY_Address > 31)
    {
        LOG_E("cannot comm with PHY!");
        while (1);
    }

    // SetReset
    if (!(ETH_WritePhyRegister(PHY_Address, PHY_BCR, (PHY_RESET|PHY_AUTONEGO))))
    {
        return ETH_ERROR;
    }
    ETH_ReadPhyRegister(PHY_Address, PHY_BCR);

    switch (PHY_TYPE)
    {
    case PHY_TYPE_LAN8710:
        phy_status_reg = 0x1f;
        phy_speed_bit  = 0x0004;
        phy_duplex_bit = 0x0010;
        break;
    case PHY_TYPE_DP83848:
        phy_status_reg = 0x10;
        phy_speed_bit  = 0x0002;
        phy_duplex_bit = 0x0004;
        // we can configure RMII mode through PHY register for DP83848
        if (ETH_INTERFACE == ETH_INTERFACE_RMII)
        {
            ETH_WritePhyRegister(PHY_Address, 0x17, 0x21);
        }
        break;
    case PHY_TYPE_KSZ8041:
        phy_status_reg = 0x1f;
        phy_speed_bit  = 0x0004;
        phy_duplex_bit = 0x0010;
        break;
    case PHY_TYPE_DM9161:
        phy_status_reg = 0x11;
        phy_speed_bit  = 0x00;
        phy_duplex_bit = 0x00;
        break;
    case PHY_TYPE_IP101GR:
        phy_status_reg = 0x1E;
        phy_speed_bit  = 0x0001;
        phy_duplex_bit = 0x0004;
        break;
    default:
        while (1)
            ;
    }

    if (ETH_InitStruct->AutoNegotiation != ETH_AUTONEG_DISABLE)
    {
#if 1
        // We wait for linked satus
        timeout = 0;
        do
        {
            timeout++;
        } while (!(ETH_ReadPhyRegister(PHY_Address, PHY_BSR) & PHY_LINKED_STATUS) && (timeout < PHY_READ_TO));
#endif
        if (!(ETH_WritePhyRegister(PHY_Address, PHY_BCR, PHY_AUTONEGO)))
        {
            return ETH_ERROR;
        }
        ETH_ReadPhyRegister(PHY_Address, PHY_BCR);
        // Enable Auto-Negotiation
        timeout = 0;
        do
        {
            timeout++;
        } while (!(ETH_ReadPhyRegister(PHY_Address, PHY_BSR) & PHY_AUTONEGO_COMPLETE)
                 && (timeout < (uint32_t)PHY_READ_TO));

        // Read Phy Status that is the result of the autonegotiation//786d
        RegValue = ETH_ReadPhyRegister(PHY_Address, phy_status_reg);
        if (PHY_TYPE != PHY_TYPE_DM9161)
        {
            if ((RegValue & phy_duplex_bit) != (uint32_t)RESET)
            {
                ETH_InitStruct->DuplexMode = ETH_DUPLEX_MODE_FULL;
            }
            else
            {
                ETH_InitStruct->DuplexMode = ETH_DUPLEX_MODE_HALF;
            }

            if (RegValue & phy_speed_bit)
            {
                ETH_InitStruct->SpeedMode = ETH_SPEED_MODE_10M;
            }

            else
            {
                ETH_InitStruct->SpeedMode = ETH_SPEED_MODE_100M;
            }
        }
        else
        {
            if (RegValue & 0x8000) // bit15
            {
                ETH_InitStruct->DuplexMode = ETH_DUPLEX_MODE_FULL;
                ETH_InitStruct->SpeedMode  = ETH_SPEED_MODE_100M;
            }
            else if (RegValue & 0x4000) // bit14
            {
                ETH_InitStruct->DuplexMode = ETH_DUPLEX_MODE_HALF;
                ETH_InitStruct->SpeedMode  = ETH_SPEED_MODE_100M;
            }
            else if (RegValue & 0x2000) // bit13
            {
                ETH_InitStruct->DuplexMode = ETH_DUPLEX_MODE_FULL;
                ETH_InitStruct->SpeedMode  = ETH_SPEED_MODE_10M;
            }
            else if (RegValue & 0x1000) // bit12
            {
                ETH_InitStruct->DuplexMode = ETH_DUPLEX_MODE_HALF;
                ETH_InitStruct->SpeedMode  = ETH_SPEED_MODE_10M;
            }
            else
            {
                while (1)
                    ;
            }
        }
    }
    else
    {
        RegValue = 0;
        if (ETH_InitStruct->DuplexMode == ETH_DUPLEX_MODE_FULL)
        {
            RegValue |= 0x100;
        }
        if (ETH_InitStruct->SpeedMode == ETH_SPEED_MODE_100M)
        {
            RegValue |= 0x2000;
        }
        if (!ETH_WritePhyRegister(PHY_Address, PHY_BCR, RegValue))
        {
            return ETH_ERROR;
        }

        // We wait for linked satus
        timeout = 0;
        do
        {
            timeout++;
        } while (!(ETH_ReadPhyRegister(PHY_Address, PHY_BSR) & PHY_LINKED_STATUS) && (timeout < PHY_READ_TO));
    }
    return ETH_SUCCESS;
}

//uint32_t InitPHY(ETH_InitType* ETH_InitStruct)
//{
//	rt_thread_mdelay(3000);
//	return ETH_SUCCESS;
//}


extern void delay_ms(uint32_t ms);
extern void phy_reset(void);
extern void n32_msp_eth_init(void *Instance);
/* EMAC initialization function */
static rt_err_t rt_n32g45x_eth_init(rt_device_t dev)
{
    uint8_t rval = 0;
    ETH_InitType ETH_InitStructure = {0};
		
    RCC_EnableAHBPeriphClk(RCC_AHB_PERIPH_ETHMAC, ENABLE);
		
    RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_AFIO |  RCC_APB2_PERIPH_GPIOA | RCC_APB2_PERIPH_GPIOB |
					RCC_APB2_PERIPH_GPIOC | RCC_APB2_PERIPH_GPIOD | RCC_APB2_PERIPH_GPIOE |
					RCC_APB2_PERIPH_GPIOF | RCC_APB2_PERIPH_GPIOG, ENABLE);
		
    //ETH_ConfigGpio( n32g45x_eth_device.eth_interface_mode, 0);
		
		n32_msp_eth_init(NULL);
		
    phy_reset();
		
    // MII/RMII should be configured before enabling ETH clock
		if(n32g45x_eth_device.eth_interface_mode == ETH_INTERFACE_RMII)
		{
			GPIO_ETH_ConfigMediaInterface( GPIO_ETH_RMII_CFG );
		}else
		{
			GPIO_ETH_ConfigMediaInterface( GPIO_ETH_MII_CFG );
		}
		
//		// ETH->MACPMTCTRLSTS |= 0x80000000;
//		NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
//		NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x0);
		/* Enable the Ethernet global Interrupt */
		NVIC_InitType NVIC_InitStructure;
		NVIC_InitStructure.NVIC_IRQChannel                   = ETH_IRQn;
		NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
		NVIC_InitStructure.NVIC_IRQChannelSubPriority        = 0;
		NVIC_InitStructure.NVIC_IRQChannelCmd                = ENABLE;
		NVIC_Init(&NVIC_InitStructure);
		
		ETH_DeInit();        // Reset AHB ETH clock
		
    ETH_SoftwareReset(); // software reset ETH module
		
    while (ETH_GetSoftwareResetStatus() == SET)
		{
			delay_ms(100);
		}; // wait for reset over
		
    ETH_InitStruct(&ETH_InitStructure);

    // set ETH MAC parameters
    ETH_InitStructure.AutoNegotiation          = ETH_AUTONEG_ENABLE;
    ETH_InitStructure.LoopbackMode             = ETH_LOOPBACK_MODE_DISABLE;
    ETH_InitStructure.RetryTransmission        = ETH_RETRY_TRANSMISSION_DISABLE;
    ETH_InitStructure.AutomaticPadCRCStrip     = ETH_AUTO_PAD_CRC_STRIP_DISABLE;
    ETH_InitStructure.RxAll                    = ETH_RX_ALL_DISABLE;
    ETH_InitStructure.BroadcastFramesReception = ETH_BROADCAST_FRAMES_RECEPTION_ENABLE;
    ETH_InitStructure.PromiscuousMode          = ETH_PROMISCUOUS_MODE_DISABLE;
    ETH_InitStructure.MulticastFramesFilter    = ETH_MULTICAST_FRAMES_FILTER_NONE;
    ETH_InitStructure.UnicastFramesFilter      = ETH_UNICAST_FRAMES_FILTER_PERFECT;
#ifdef RT_LWIP_USING_HW_CHECKSUM
    ETH_InitStructure.ChecksumOffload = ETH_CHECKSUM_OFFLOAD_ENABLE;
    ETH_InitStructure.DropTCPIPChecksumErrorFrame = ETH_DROP_TCPIP_CHECKSUM_ERROR_FRAME_ENABLE;
#else
    ETH_InitStructure.ChecksumOffload = ETH_CHECKSUM_OFFLOAD_DISABLE;
    ETH_InitStructure.DropTCPIPChecksumErrorFrame = ETH_DROP_TCPIP_CHECKSUM_ERROR_FRAME_DISABLE;
#endif
		
//    ETH_InitStructure.ChecksumOffload = ETH_CHECKSUM_OFFLOAD_ENABLE;
//    ETH_InitStructure.DropTCPIPChecksumErrorFrame = ETH_DROP_TCPIP_CHECKSUM_ERROR_FRAME_ENABLE;

    ETH_InitStructure.RxStoreForward              = ETH_RX_STORE_FORWARD_DISABLE;
    ETH_InitStructure.TxStoreForward              = ETH_TX_STORE_FORWARD_DISABLE;

    ETH_InitStructure.ForwardErrorFrames          = ETH_FORWARD_ERROR_FRAMES_DISABLE;
    ETH_InitStructure.ForwardUndersizedGoodFrames = ETH_FORWARD_UNDERSIZED_GOOD_FRAMES_DISABLE;
    ETH_InitStructure.SecondFrameOperate          = ETH_SECOND_FRAME_OPERATE_ENABLE;
    ETH_InitStructure.AddrAlignedBeats            = ETH_ADDR_ALIGNED_BEATS_ENABLE;
    ETH_InitStructure.FixedBurst                  = ETH_FIXED_BURST_ENABLE;
    ETH_InitStructure.RxDMABurstLen               = ETH_RX_DMA_BURST_LEN_32BEAT;
    ETH_InitStructure.TxDMABurstLen               = ETH_TX_DMA_BURST_LEN_32BEAT;
    ETH_InitStructure.DMAArbitration              = ETH_DMA_ARBITRATION_ROUND_ROBIN_RXTX_2_1;

    rval = ETH_Init(&ETH_InitStructure, InitPHY);
    if (rval) // init succeed
    {
        ETH_EnableDmaInt(ETH_DMA_INT_NIS | ETH_DMA_INT_RX, ENABLE); // Enable rx interrupt
	
				ETH_SetMacAddr(ETH_MAC_ADDR0, (uint8_t *)&(n32g45x_eth_device.dev_addr[0]));
				ETH_ConfigDmaTxDescInChainMode(g_TxDesc, g_TxBuf, ETH_TX_BUF_SIZE, ETH_TXBUFNB);
				ETH_ConfigDmaRxDescInChainMode(g_RxDesc, g_RxBuf, ETH_RX_BUF_SIZE, ETH_RXBUFNB);
				
				ETH_EnableTxRx();
			
		#ifdef RT_LWIP_USING_HW_CHECKSUM
				for (int i = 0; i < ETH_TXBUFNB; i++)
				{
						ETH_ConfigDmaTxDescChecksumInsertion(&g_TxDesc[i], ETH_DMA_TX_DESC_CHECKSUM_TCPUDPICMP_FULL);
				}
		#endif
				LOG_I("emac hardware start");
    }else
		{
        LOG_E("emac hardware start faild");
        return -RT_ERROR;
		}
		
    return RT_EOK;
}

static rt_err_t rt_n32g45x_eth_open(rt_device_t dev, rt_uint16_t oflag)
{
    LOG_D("emac open");
    return RT_EOK;
}

static rt_err_t rt_n32g45x_eth_close(rt_device_t dev)
{
    LOG_D("emac close");
    return RT_EOK;
}

static rt_size_t rt_n32g45x_eth_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size)
{
    LOG_D("emac read");
    rt_set_errno(-RT_ENOSYS);
    return 0;
}

static rt_size_t rt_n32g45x_eth_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size)
{
    LOG_D("emac write");
    rt_set_errno(-RT_ENOSYS);
    return 0;
}

static rt_err_t rt_n32g45x_eth_control(rt_device_t dev, int cmd, void *args)
{
    switch (cmd)
    {
    case NIOCTL_GADDR:
        /* get mac address */
        if (args) rt_memcpy(args, n32g45x_eth_device.dev_addr, 6);
        else return -RT_ERROR;
        break;
    default :
        break;
    }
    return RT_EOK;
}


static uint8_t eth_tx_Buffer[ETH_TX_BUF_SIZE] = {0};
/* ethernet device interface */
/* transmit data*/
rt_err_t rt_n32g45x_eth_tx(rt_device_t dev, struct pbuf *p)
{
    uint32_t res;
    rt_err_t ret = RT_EOK;
	
    struct pbuf* q;
    int len	= 0;
		
    for (q = p; q != NULL; q = q->next)
    {
        rt_memcpy((uint8_t*)&(eth_tx_Buffer[len]), q->payload, q->len);
        len += q->len;
    }

#ifdef ETH_TX_DUMP
    /* Prepare transmit descriptors to give to DMA */
    /* TODO Optimize data send speed*/
    LOG_I("transmit frame length :%d", len);
		
    dump_hex(eth_tx_Buffer, len);
#endif

    res = ETH_TxPacket(eth_tx_Buffer, len);
    if (res == ETH_ERROR)
    {
			LOG_E("eth transmit frame len:%d faild: %d",len, res);
        ret = -RT_ERROR; // error status
    }
    return ret;
}

static uint8_t eth_rx_Buffer[ETH_TX_BUF_SIZE] = {0};
/* receive data*/
struct pbuf *rt_n32g45x_eth_rx(rt_device_t dev)
{
    struct pbuf *p = NULL, *q = NULL;
    uint32_t len;
    int ret;
    int l = 0;
    p   = NULL;
    len = ETH_GetRxPacketSize();
    //LOG_I("receive frame len : %d", len);
    if (len == 0)
    {
        return NULL;
    }
		ret = ETH_RxPacket(eth_rx_Buffer,0);
    if (ETH_ERROR == ret)
    {
				LOG_I("RxCpltCallback err = %d", ret);
        return NULL;
    }
		
    p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL);
    if (p != NULL)
    {
        for (q = p; q != NULL; q = q->next)
        {
            memcpy((uint8_t*)q->payload, (uint8_t*)&( eth_rx_Buffer[l] ), q->len);
            l = l + q->len;
        }
    }
#ifdef ETH_RX_DUMP
    LOG_I("receive frame len : %d", len);
    dump_hex(eth_rx_Buffer, p->tot_len);
#endif
    return p;
}

/* interrupt service routine */
void ETH_IRQHandler(void)
{
    /* enter interrupt */
    rt_interrupt_enter();

    /* Frame received */
    if (ETH_GetDmaFlagStatus(ETH_DMA_FLAG_RX) == SET)
    {
			rt_err_t result;
			result = eth_device_ready(&(n32g45x_eth_device.parent));
			if (result != RT_EOK)
			{
				LOG_I("RxCpltCallback err = %d", result);
			}
    }
    /* Clear the interrupt flags. */
    /* Clear the Eth DMA Rx IT pending bits */
    ETH_ClrDmaIntPendingBit(ETH_DMA_INT_RX);
    ETH_ClrDmaIntPendingBit(ETH_DMA_INT_NIS);

    /* leave interrupt */
    rt_interrupt_leave();
}


enum {
    PHY_LINK        = (1 << 0),
    PHY_100M        = (1 << 1),
    PHY_FULL_DUPLEX = (1 << 2),
};

static void phy_linkchange()
{
    static rt_uint8_t phy_speed = 0;
    rt_uint8_t phy_speed_new = 0;
    rt_uint32_t status;
		
		status = phy_read_reg(n32g45x_eth_device.phy_addr, PHY_BASIC_STATUS_REG);
    LOG_D("phy basic status reg is 0x%X", status);

    if (status & (PHY_AUTONEGO_COMPLETE_MASK | PHY_LINKED_STATUS_MASK))
    {
        rt_uint32_t status_reg = 0;

        phy_speed_new |= PHY_LINK;

				status_reg = phy_read_reg(n32g45x_eth_device.phy_addr, PHY_Status_REG);
        LOG_D("phy control status reg is 0x%X", status_reg);

        if (PHY_Status_SPEED_100M(status_reg))
        {
            phy_speed_new |= PHY_100M;
        }

        if (PHY_Status_FULL_DUPLEX(status_reg))
        {
            phy_speed_new |= PHY_FULL_DUPLEX;
        }
    }

    if (phy_speed != phy_speed_new)
    {
        phy_speed = phy_speed_new;
        if (phy_speed & PHY_LINK)
        {
            LOG_I("link up");
            if (phy_speed & PHY_100M)
            {
                LOG_I("100Mbps");
                n32g45x_eth_device.eth_link_speed = ETH_SPEED_MODE_100M;
            }
            else
            {
                n32g45x_eth_device.eth_link_speed = ETH_SPEED_MODE_10M;
                LOG_I("10Mbps");
            }

            if (phy_speed & PHY_FULL_DUPLEX)
            {
                LOG_I("full-duplex");
                n32g45x_eth_device.eth_link_mode = ETH_DUPLEX_MODE_FULL;
            }
            else
            {
                LOG_I("half-duplex");
                n32g45x_eth_device.eth_link_mode = ETH_DUPLEX_MODE_HALF;
            }

            /* send link up. */
            eth_device_linkchange(&n32g45x_eth_device.parent, RT_TRUE);
        }
        else
        {
            LOG_I("link down");
            eth_device_linkchange(&n32g45x_eth_device.parent, RT_FALSE);
        }
    }
}

#ifdef PHY_USING_INTERRUPT_MODE
static void eth_phy_isr(void *args)
{
    rt_uint32_t status = 0;

    HAL_ETH_ReadPHYRegister(&EthHandle, PHY_INTERRUPT_FLAG_REG, (uint32_t *)&status);
    LOG_D("phy interrupt status reg is 0x%X", status);

    phy_linkchange();
}
#endif /* PHY_USING_INTERRUPT_MODE */

static void phy_monitor_thread_entry(void *parameter)
{
    uint16_t status = 0;
    uint16_t phy_id = 0;
    uint8_t phy_addr = 0xFF;
    uint8_t detected_count = 0;

    while(phy_addr == 0xFF)
    {
        /* phy search */
        for (uint8_t i = 0; i <= 0x1F; i++)
        {
					phy_id = phy_read_reg(i,PHY_ID1_REG);
					if (phy_id != 0xFFFF && phy_id != 0x0000)
					{
							phy_addr = i;
							break;
					}
        }
        detected_count++;
        rt_thread_mdelay(1000);

        if (detected_count > 10)
        {
            LOG_E("No PHY device was detected, please check hardware!");
        }
    }

    LOG_I("Found a phy, address:0x%02X  id:0x%04X", phy_addr,phy_id);
		
		n32g45x_eth_device.phy_addr = phy_addr;
		n32g45x_eth_device.phy_id = phy_id;

    /* RESET PHY */
    LOG_I("RESET PHY!");
		phy_write_reg(n32g45x_eth_device.phy_addr,PHY_BASIC_CONTROL_REG, PHY_RESET_MASK);
		status = phy_read_reg(n32g45x_eth_device.phy_addr, PHY_BASIC_CONTROL_REG);
    rt_thread_mdelay(3000);
		
		do{
			rt_thread_mdelay(100);
			status = phy_read_reg(n32g45x_eth_device.phy_addr, PHY_BASIC_STATUS_REG);
		}while(status & PHY_LINKED_STATUS_MASK);
		
    LOG_I("LINK!");
		phy_write_reg(n32g45x_eth_device.phy_addr,PHY_BASIC_CONTROL_REG, PHY_AUTO_NEGOTIATION_MASK);
		status = phy_read_reg(n32g45x_eth_device.phy_addr, PHY_BASIC_CONTROL_REG);
		
//		phy_write_reg(n32g45x_eth_device.phy_addr,PHY_BASIC_CONTROL_REG, (PHY_RESET_MASK | PHY_AUTO_NEGOTIATION_MASK));
//		rt_thread_mdelay(3000);
		
    phy_linkchange();
#ifdef PHY_USING_INTERRUPT_MODE
    /* configuration intterrupt pin */
    rt_pin_mode(PHY_INT_PIN, PIN_MODE_INPUT_PULLUP);
    rt_pin_attach_irq(PHY_INT_PIN, PIN_IRQ_MODE_FALLING, eth_phy_isr, (void *)"callbackargs");
    rt_pin_irq_enable(PHY_INT_PIN, PIN_IRQ_ENABLE);

    /* enable phy interrupt */
    HAL_ETH_WritePHYRegister(&EthHandle, PHY_INTERRUPT_MASK_REG, PHY_INT_MASK);
#if defined(PHY_INTERRUPT_CTRL_REG)
    HAL_ETH_WritePHYRegister(&EthHandle, PHY_INTERRUPT_CTRL_REG, PHY_INTERRUPT_EN);
#endif
#else /* PHY_USING_INTERRUPT_MODE */
    n32g45x_eth_device.poll_link_timer = rt_timer_create("phylnk", (void (*)(void*))phy_linkchange,NULL, RT_TICK_PER_SECOND, RT_TIMER_FLAG_PERIODIC);
    if (!n32g45x_eth_device.poll_link_timer || rt_timer_start(n32g45x_eth_device.poll_link_timer) != RT_EOK)
    {
        LOG_E("Start link change detection timer failed");
    }
#endif /* PHY_USING_INTERRUPT_MODE */
}

/* Register the EMAC device */
static int n32g45x_eth0_init(uint8_t *mac_addr,const char *eth_name)
{
    rt_err_t state = RT_EOK;
	
    /* OUI. */
    n32g45x_eth_device.dev_addr[0] = mac_addr[0];
    n32g45x_eth_device.dev_addr[1] = mac_addr[1];
    n32g45x_eth_device.dev_addr[2] = mac_addr[2];
    n32g45x_eth_device.dev_addr[3] = mac_addr[3];
    n32g45x_eth_device.dev_addr[4] = mac_addr[4];
    n32g45x_eth_device.dev_addr[5] = mac_addr[5];

    n32g45x_eth_device.parent.parent.init       = rt_n32g45x_eth_init;
    n32g45x_eth_device.parent.parent.open       = rt_n32g45x_eth_open;
    n32g45x_eth_device.parent.parent.close      = rt_n32g45x_eth_close;
    n32g45x_eth_device.parent.parent.read       = rt_n32g45x_eth_read;
    n32g45x_eth_device.parent.parent.write      = rt_n32g45x_eth_write;
    n32g45x_eth_device.parent.parent.control    = rt_n32g45x_eth_control;
    n32g45x_eth_device.parent.parent.user_data  = RT_NULL;

    n32g45x_eth_device.parent.eth_rx     = rt_n32g45x_eth_rx;
    n32g45x_eth_device.parent.eth_tx     = rt_n32g45x_eth_tx;

    /* register eth device */
    state = eth_device_init(&(n32g45x_eth_device.parent), eth_name);
    if (RT_EOK == state)
    {
        LOG_D("emac device init success");
    }
    else
    {
        LOG_E("emac device init faild: %d", state);
        state = -RT_ERROR;
        goto __exit;
    }
    /* start phy monitor */
    rt_thread_t tid;
    tid = rt_thread_create("phy",phy_monitor_thread_entry,RT_NULL,1024,RT_THREAD_PRIORITY_MAX - 2,2);
    if (tid != RT_NULL)
    {
        rt_thread_startup(tid);
    }
    else
    {
        state = -RT_ERROR;
    }
__exit:
    if (state != RT_EOK)
    {

    }
    return state;
}

uint8_t mac_address[] = {0x20, 0x34, 0x56, 0x78, 0x9a, 0xbc};

/* Register the EMAC device */
static int rt_hw_n32g45x_eth_init(void)
{
	return n32g45x_eth0_init(mac_address,"e0");
}
//INIT_DEVICE_EXPORT(rt_hw_n32g45x_eth_init);
#endif
